Skip to content
View Article Network

Applications of Web.config (App.config) in .NET Framework

Publishing Different Configuration Files for Different Build Configurations

Normally, Web.config (App.config) settings for local development, testing environments, and production environments are different. It is too cumbersome to manually change settings to match the environment every time you update the program. If you choose not to update the Web.config (App.config) during a program update, you might forget to adjust the configuration file content on the test/development environment when new settings are added or modified.

One approach is to overwrite the configuration file with every program update, but to generate the environment-specific configuration file during the publishing process.

Build Configurations

If you need to build a project based on different settings, you will need to set up Build Configurations. Different settings cover producing different configuration files, compilation options, or producing different compilation results based on different project constants. For details, please refer to "Understand build configurations".

The default build configurations are debug and release, which can be switched in the IDE.

vs build configuration menu

Web Project Publishing

Regarding project publishing, this article only covers folder publishing. I have not explored other publishing methods. If needed, please refer to "Quickstart: Deploy an ASP.NET web app".

Adding a Publish Profile

  1. Right-click the project and select "Publish".

    vs project publish menu

  2. Click "New publish profile".

    vs publish new profile

  3. This article only discusses folder publishing, so select "Folder".

    vs publish target folder

  4. Set the output folder. I am accustomed to setting it to bin\Publish\{ConfigurationName}. Here, I set it to bin\Publish\Release.

    vs publish location setting

  5. After adding, it is recommended to rename the publish profile to match the configuration name or include a brief description.

    vs publish profile rename

  6. Edit the publish settings. Basic settings can be edited via the UI. I check all of them here, but in practice, please adjust according to your needs. For example, if you are publishing to a website and set it to "Delete all existing files in the destination prior to publish," it will cause temporary files stored on the site (e.g., uploaded files) to be deleted as well.

    vs publish profile settings

  7. You can directly modify the XML to change publish settings, for example, by adding <ExcludeFilesFromDeployment>packages.config;Scripts\_references.js</ExcludeFilesFromDeployment> to exclude specific files during publishing. As for the specific publish settings available in XML, I have not found relevant documentation on MSDN.

    vs publish profile preview

TIP

When a publish profile is created, another file named "{Profile}.pubxml.user" is generated. The purpose of this file is unclear; it actually looks more like a publish log, which contradicts the description on MSDN. In any case, this file should not be included in version control. In fact, the default Visual Studio ".gitignore" also ignores user setting files with the ".user" extension.

Web.config Transform

When creating a new Web project, you will find three Config files in the project root by default: "Web.config", "Web.Debug.config", and "Web.Release.config". However, when building the project, you will find that the "Web.config" settings are always used. The other two Config files are actually used to transform parts of "Web.config" during publishing.

  • If you find that a "Web.{ConfigurationName}.config" file corresponding to a build configuration is missing (possibly due to accidental deletion or creating a new build configuration), you can right-click on "Web.config" and select "Add Config Transform". After clicking, the missing Config file will be created.

vs add config transform

  • Right-click on "Web.{ConfigurationName}.config" and select "Preview Transform" to view the transformation results.

vs preview transform

  • Transform replacement mechanism. Transform uses XML structure to locate content to be replaced. The xdt:Transform attribute sets the replacement method. Let's use the following example content for explanation.

    xml
    <a>
      <b>
        <c>
          <e name="e1" value="v1"></e>
          <e name="e2" value="v2"></e>
        </c>
        <d></d>
      </b>
    </a>

    If you want to replace the content of the <d> node, you need to prepare an XML containing the <d> node and all parent nodes, with the structure as follows:

    xml
    <a>
      <b>
        <d xdt:Transform="Replace"></d>
      </b>
    </a>

    For nodes like <e> where there are multiple nodes with the same name under the same parent, you need to use the xdt:Locator attribute to locate them. For example, if you want to replace <e name="e1"></e>, the XML content would be:

    xml
    <a>
      <b>
        <c>
          <e name="e1" value="v11" xdt:Locator="Match(name)"
              xdt:Transform="SetAttributes"></e>
        </c>
      </b>
    </a>
  • Common transformation settings.

    • Replacing connectionStrings.

      xml
      <!--Replace all connectionStrings-->
      <connectionStrings xdt:Transform="Replace">
        <add name="Default" connectionString="{connectionString}"
             providerName="System.Data.SqlClient" />
      </connectionStrings>
      
      <!--Replace only a single string-->
      <add name="Default" connectionString="{connectionString}"
           providerName="System.Data.SqlClient"
           xdt:Locator="Match(name)" xdt:Transform="SetAttributes" />
    • Replacing appSettings.

      xml
      <!--It is recommended to replace individually rather than replacing the entire appSettings-->
      <add key="key" value="value1"
           xdt:Locator="Match(key)" xdt:Transform="SetAttributes" />
    • Common system.web replacement items.

      xml
      <!--Change mode="Off" to RemoteOnly, and add defaultRedirect -->
      <customErrors mode="RemoteOnly" xdt:Transform="SetAttributes" />
      <!--Change enableVersionHeader to false-->
      <httpRuntime enableVersionHeader="false"
                   xdt:Transform="SetAttributes(enableVersionHeader)" />
      <!--Change requireSSL to true-->
      <httpCookies requireSSL="true"
                   xdt:Transform="SetAttributes(requireSSL)" />
      <!--Remove debug="true" setting-->
      <compilation xdt:Transform="RemoveAttributes(debug)" />

For the complete Transform syntax, please refer to "Web.config Transformation Syntax for Web Project Deployment Using Visual Studio".

Publishing

After selecting publish, you will see the output files in the target folder. At this point, you will find that the generated "Web.config" contains the merged content.

vs publish output

Example

Original Web.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=SQLServer;Initial Catalog=MyDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="MyKey" value="MyValue" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.8.1" />
    <customErrors mode="Off" />
    <httpRuntime targetFramework="4.8.1" />
    <httpCookies requireSSL="false" />
  </system.web>
  <!--Remaining parts are unchanged, omitted here-->
</configuration>

Web.Release.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
         xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
    <customErrors mode="RemoteOnly" xdt:Transform="SetAttributes" />
    <httpRuntime enableVersionHeader="false"
                 xdt:Transform="SetAttributes(enableVersionHeader)" />
    <!--Change requireSSL to true-->
    <httpCookies requireSSL="true"
                 xdt:Transform="SetAttributes(requireSSL)" />
  </system.web>
</configuration>

Published Web.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="MyKey" value="MyReleaseValue" />
  </appSettings>
  <system.web>
    <compilation targetFramework="4.8.1" />
    <customErrors mode="RemoteOnly" />
    <httpRuntime targetFramework="4.8.1" enableVersionHeader="false" />
    <httpCookies requireSSL="true" />
  </system.web>
  <runtime>
  <!--Remaining parts are unchanged, omitted here-->
</configuration>

Non-Web Project Publishing

Non-Web projects do not support "App.config" Transform by default, so you need to install other packages to handle it.

Packages

  • Visual Studio: Install "SlowCheetah" in extensions.
  • NuGet: Install "Microsoft.VisualStudio.SlowCheetah" on the project where you want to use Config Transform.

After installing "SlowCheetah" in Visual Studio, an "Add Transform" option will be added to the right-click menu of App.config in non-Web projects, allowing you to generate Config files for other configurations.

vs slowcheetah add transform

If "Microsoft.VisualStudio.SlowCheetah" is not installed via NuGet, it will prompt you and install it automatically.

vs slowcheetah nuget prompt

TIP

Actually, Visual Studio has another extension called "ConfigTransformation" that supports Config Transform and does not require installing additional NuGet packages in the project. However, Visual Studio 2022 does not support this extension, and it can only perform transformations on XML with a complete Config structure (root node is <configuration />).

Build

Publishing for non-Web projects in ".NET Framework" uses ClickOnce and cannot be published to a folder like Web projects. However, its build will trigger Config Transform, so after selecting the configuration to publish, just build the project. It is recommended to delete the "bin" folder before building the project.

vs build non web project

Example

Original App.config

xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=SQLServer;Initial Catalog=MyDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyValue" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
  </startup>
</configuration>

App.Release.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
         xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
</configuration>

Built {ProjectName}.exe.config

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" />
  </connectionStrings>
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
  </startup>
</configuration>

Sharing Configuration Files Across Different Projects

If multiple projects under the same solution need to connect to the same database, such as a website, multiple schedulers, or Web APIs, and every time the database environment changes, all projects need to be modified, you can consider placing <connectionStrings /> in a separate file to allow multiple projects to reference it.

Setup Steps

Create a Library project and add three configuration files: "AppGlobal.config", "Connection.config", and "Smtp.config". All three files are set up with Config Transform.

For convenience, these three files will be collectively referred to as external configuration files, and those produced by Config Transform will be referred to as external configuration settings files.

vs solution explorer structure

Setting Config Content

Here, we only use AppGlobal.config as an example.

AppGlobal.config

xml
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
  <add key="Test" value="TestValue1" />
</appSettings>
xml
<?xml version="1.0" encoding="utf-8" ?>
<!--The root node must contain the attribute xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" -->
<appSettings xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <add key="Test" value="TestReleaseValue1" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>

Adding References from a Web Project

  • Install "Microsoft.VisualStudio.SlowCheetah" to handle the transformation of these external configuration files.
  • In addition to adding the Library project reference to the Web project.
  • You also need to add file links to the three newly created Config files and their related configuration files. Select "Add Existing Item" in the Web project.

vs add existing item menu

Use the "Add as Link" method to add external configuration files and related external configuration settings files.

vs add as link

  • Open the project file (csproj) and make the following modifications:
    • Add <TransformOnBuild>true</TransformOnBuild> to indicate that Config Transform should be executed during build.
    • Add <CopyToOutputDirectory>Always</CopyToOutputDirectory> to external configuration files.
    • Change external configuration settings files from <Content /> nodes to <None /> nodes to prevent them from being published as well.
    • Add <DependentUpon /> nodes to external configuration settings files so that they are displayed nested under the external configuration files in Visual Studio.
xml
<Content Include="..\BuildConfigSample.Library\AppGlobal.config">
  <Link>AppGlobal.config</Link>
  <TransformOnBuild>true</TransformOnBuild>
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="..\BuildConfigSample.Library\AppGlobal.Debug.config">
  <Link>AppGlobal.Debug.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>
<None Include="..\BuildConfigSample.Library\AppGlobal.Release.config">
  <Link>AppGlobal.Release.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>
<!--Connection.config and Smtp.config are modified in the same way-->
  • Modify "Web.config" and "Web.Release.config" to externally reference the external configuration files.
    • The difference between configSource and file attributes is that the latter allows adding other settings for merging.

    • <connectionStrings /> and <smtp /> cannot use file.

    • <appSettings /> allows using configSource, but it is recommended to use file.

    • If the key in the externally referenced file for <appSettings /> is the same as a key added by yourself, the externally referenced value will be used, so you cannot override settings by defining the same key.

      • Using the link method to add configuration files means there are no files in the project root, causing "Web.config" to be unable to find the external configuration file in the same folder when the Web is running in Debug mode. Therefore, reference the files under "bin" instead (during setup, we set the external configuration files to "Copy to Output Directory").

      Web.config

      xml
      <configuration>
        <connectionStrings configSource="bin\Connection.config" />
        <appSettings file="bin\AppGlobal.config">
          <add key="webpages:Version" value="3.0.0.0" />
          <add key="webpages:Enabled" value="false" />
          <add key="ClientValidationEnabled" value="true" />
          <add key="UnobtrusiveJavaScriptEnabled" value="true" />
          <add key="MyKey" value="MyValue" />
        </appSettings>
        <system.net>
          <mailSettings>
            <smtp configSource="bin\Smtp.config" />
          </mailSettings>
        </system.net>
        <!--Remaining content omitted-->
      </configuration>

      Web.Release.config

      xml
      <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
        <connectionStrings configSource="Connection.config" xdt:Transform="SetAttributes" />
        <appSettings file="AppGlobal.config" xdt:Transform="SetAttributes">
          <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
        </appSettings>
        <system.net>
          <mailSettings>
            <smtp configSource="Smtp.config" xdt:Transform="Replace" />
          </mailSettings>
        </system.net>
        <!--Remaining content omitted-->
      </configuration>

Adding References from a Non-Web Project

The previous operation steps are the same as for Web projects, but the modifications to the project file and App.config content are slightly different.

The external configuration files in the project file use <None /> nodes.

xml
<None Include="..\BuildConfigSample.Library\AppGlobal.config">
  <Link>AppGlobal.config</Link>
  <TransformOnBuild>true</TransformOnBuild>
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="..\BuildConfigSample.Library\AppGlobal.Debug.config">
  <Link>AppGlobal.Debug.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>
<None Include="..\BuildConfigSample.Library\AppGlobal.Release.config">
  <Link>AppGlobal.Release.config</Link>
  <DependentUpon>AppGlobal.config</DependentUpon>
  <TransformOnBuild>true</TransformOnBuild>
</None>

TIP

The <None /> node and <Content /> node correspond to the build actions "None" and "Content" respectively. "Content" is for Web use. For details, please refer to "Build action values".

App.config

Because in Debug mode, the configuration file itself uses the "{ProjectName}.exe.config" file under "bin{ConfigurationName}", the path does not need to be specified under "bin".

xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings configSource="Connection.config" />
  <appSettings file="AppGlobal.config">
    <add key="MyKey" value="MyValue" />
  </appSettings>
  <system.net>
    <mailSettings>
      <smtp configSource="Smtp.config" />
    </mailSettings>
  </system.net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
  </startup>
</configuration>
App.Release.config

After changing to external referencing, App.Release.config no longer needs to transform the connectionStrings content.

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="MyKey" value="MyReleaseValue" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
</configuration>

WARNING

If you are using Entity Framework's Database First, the connection string in the project's App.config cannot be set using an external reference file, otherwise the Entity Framework UI will not be able to find the already configured connection string.

Breaking the Inheritance Relationship of Web.config

IIS allows building sub-websites under a website. In this case, the sub-website's Web.config will inherit the parent website's settings. Sometimes conflicts occur, especially when there is a difference in Framework versions between the two. In this case, you can use <location> in the parent website to break the inheritance. The syntax is as follows:

xml
<location path="." inheritInChildApplications="false">
  <system.web>
    <!--...Internal code...-->
  </system.web>
</location>

TIP

Theoretically, writing <location /> outside of <system.web /> is sufficient, but if necessary, it can also be added outside other nodes. However, if the project uses Entity Framework's Database First, do not write it outside the <connectionStrings /> layer, otherwise it will cause the Entity Framework UI to be unable to read the configured connection string (this problem again, orz).

Change Log

  • 2022-11-10 Initial version of the document created.